/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFGana
  \brief The main frame of the ETFAna offline GUI
  Currently there is only a matrix of buttons each of which calls explicitly a
  shell command which opens another termianl and executes a certain ROOT macro.
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-05-07
  \date 2024-05-14 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2024-05-07   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <errno.h>
#include <iostream>
#include <unistd.h>
#include <TApplication.h>
#include <TGClient.h>
#include <TGButton.h>
#include <TGLayout.h>
#include <TGProgressBar.h>

#include "ETFGana.h"
#include "ETFGanaPre.h"
#include "ETFGPrompt.h"
#include "ETFMsg.h"

using std::cout;
using std::endl;

#define er ETFMsg::Error
#define form ETFMsg::Form

unsigned long ETFGana::kGanaMagenta, ETFGana::kGanaGreen, ETFGana::kGanaRed,
  ETFGana::kGanaWhite, ETFGana::kGanaYellow, ETFGana::kGanaPink,
  ETFGana::kGanaCyan, ETFGana::kGanaPurple, ETFGana::kGanaBlue;

ETFGana::ETFGana(const string &rtf, int w, int h)
: TGMainFrame(gClient->GetRoot(), w, h), fUserOpt(0), fWidth(w), fHeight(h),
fGanaPre(0){
  SetROOTFile(rtf);
  gClient->GetColorByName("magenta", kGanaMagenta);
  gClient->GetColorByName("red", kGanaRed);
  gClient->GetColorByName("green", kGanaGreen);
  gClient->GetColorByName("white", kGanaWhite);
  gClient->GetColorByName("yellow", kGanaYellow);
  gClient->GetColorByName("pink", kGanaPink);
  gClient->GetColorByName("cyan", kGanaCyan);
  gClient->GetColorByName("purple", kGanaPurple);
  gClient->GetColorByName("blue", kGanaBlue);
} // end ctor

ETFGana::~ETFGana(){
  for(auto &p : fvvB) delete p;
} // end dtor

// create all the graphical objects
void ETFGana::Initialize(){
  SetMinHeight(600);
  SetMinWidth(500);
  // supposed for layout of group-frames
  fLF = new TGLayoutHints(kLHintsCenterX | kLHintsCenterY
    | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2);
  // supposed for layout buttons in a group-frame
  fLB = new TGLayoutHints(kLHintsTop | kLHintsCenterX
    | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2);

  // top bar for menu buttons //
  const unsigned int w = GetWidth(), h = GetHeight();
  fHBtn = new TGCompositeFrame(this, 400, 30, kHorizontalFrame | kFixedWidth
    | kFixedHeight);
  AddFrame(fHBtn, new TGLayoutHints(kLHintsTop | kLHintsLeft, 2, 2, 2, 2));
  // the exit button
  fExit = new TGTextButton(fHBtn, "E&xit");
  fHBtn->AddFrame(fExit, fLB);
  fExit->Connect("Clicked()", "ETFGana", this, "CloseWindow()");
  fExit->ChangeBackground(magenta());
  // the stop button
  fStop = new TGTextButton(fHBtn, "Sto&p");
  fStop->SetEnabled(false);
  fHBtn->AddFrame(fStop, fLB);
  fStop->Connect("Clicked()", "ETFGana", this, "Stop()");
  fStop->SetToolTipText("Just test the Popup\nueser dialog");
  // the LOGO
  fLogo = new TGTextButton(fHBtn, "&About");
  fHBtn->AddFrame(fLogo, fLB);
  fLogo->Connect("Clicked()", "ETFGana", this, "About()");
  // fLogo->ChangeBackground(green());

  // ceate first-stage ana if necessary //
  if("" == fROOTFile){ // first-stage ana has not been implemented
    fGanaPre = new ETFGanaPre(this, fWidth, fHeight);
    fGanaPre->Initialize();
    AddFrame(fGanaPre, fLF);
  } // end if

  //--- construct categories of buttons ---//
  // fPad: the button matrix container
  fPad = new TGCompositeFrame(this, w, h - fHBtn->GetHeight() - 20,
    kHorizontalFrame);
  AddFrame(fPad, fLF);
  AddButtonGroup("DAQ");
  AddButtonGroup("DET0");
  AddButtonGroup("DET1");
  AddButtonGroup("POS");
  AddButtonGroup("TRK");
  AddButtonGroup("PID");

  //--- add buttons for each category ---//
  AddButton("DAQ", "raw.C", "RawDataSta",
    "raw.C - Event Length; TOT\nChIdDistri; NofEdges");
  // AddButton("DAQ", "tTrig.C", "TimeToTrig", "tTrig.C - Time To Trig");s
  AddButton("DAQ", "amp.C", "ADC&QDC", "amp.C");
  AddButton("DAQ", "align.C", "DAQS&Align", "align.C -- Trigger Alignment Among All DAQs");
  AddButton("DAQ", "qdc.C", "QDC_DET", "qdc.C -- QDC for Tstop and TOFWall in detail");

  AddButton("DET0", "tofw.C", "TOFW", "tofw.C");
  AddButton("DET0", "t1.C", "Tstop", "t1.C");
  AddButton("DET0", "veto.C", "VETO", "veto.C - beam position shown in a TH2F object");
  AddButton("DET0", "f1_raw.C", "F1_RAW", "f1_raw.C - Raw Data of F1 T0Array");
  AddButton("DET0", "f1.C", "F1", "f1.C - A Comprehensive Check on F1 detectors");
  AddButton("DET0", "mu0.C", "MUSIC0", "mu0.C - The music0 (before Target) and the calibrated Z");
  AddButton("DET0", "mu1.C", "MUSIC1", "mu1.C - The music1 (after Target) and the calibrated Z");

  AddButton("DET1", "mul.C", "Multi_DCTa", "mul.C - Multiplicity for DCs in Target Zone");
  AddButton("DET1", "muldc.C", "Multi_dc",
    "muldc.C - Multiplicity for DCs after\nthe Dipole Magnet");
  AddButton("DET1", "muldcx.C", "Multi_dcx",
    "muldcx.C - Multiplicity for X-Wires of\nDCs after the Dipole Magnet");
  AddButton("DET1", "eff.C", "Efficiency",
    "eff.C - Tracking and detecting effciencies\n(Output in textual form on terminal)");
  AddButton("DET1", "dis.C", "Wire1D_DCTa",
    "dis.C - 1-D Fired Wire Distribution of\nDCs around Target");
  AddButton("DET1", "dis2.C", "Wire2D_DCTa",
    "dis2.C - 2-D Fired Wire Distribution of\nDCs around Target");
  AddButton("DET1", "widc.C", "Wire1D_dc",
    "widc.C - 1-D Fired Wire Distribution of\nDCs after the Dipole Magnet");
  AddButton("DET1", "widcx.C", "Wire1D_dcX",
    "widcx.C - 1-D Fired Wire Distribution of X wires\nof DCs after the Dipole Magnet");

  AddButton("POS", "wi.C", "Wire1D_DCTa",
    "wi.C - 1-D Beam Profile from Wire Position\nof DCs around the Target");
  AddButton("POS", "wi2.C", "Wire2D_DCTa",
    "wi2.C - 2-D Beam Spot from Wire Position\nof DCs around the Target");
  AddButton("POS", "p2.C", "BeamSpot2D",
    "p2.C - 2-D Beam Spot from Tracking\nby DCs around the Target");
  AddButton("POS", "ta.C", "Target2D",
    "ta.C - 2-D Beam Spot on Target and T1, and the profile of Target frame");

  AddButton("TRK", "dt.C", "DriftTime_DCTa",
    "dt.C - Drift Time Distribution of\nDCs around the Target");
  AddButton("TRK", "dtdc.C", "DriftTime_dc",
    "dtdc.C - Drift Time Distribution of\nDCs after the Dipole Magnet");
  AddButton("TRK", "dtdcx.C", "DriftTime_dcX",
    "dtdcx.C - Drift Time Distribution for X Wires\nof DCs after the Dipole Magnet");
  AddButton("TRK", "nh.C", "NofHits/Trk_DCTa",
    "nh.C - Number of Hits Per Track\nfor DCs around the Target");
  AddButton("TRK", "rt2.C", "R-T_DCTa",
    "rt2.C - r+chi v.s. drift time\nfor DCs around the Target");
  AddButton("TRK", "rt2dc.C", "R-T_dc",
    "rt2dc.C - r+chi v.s. drift time for\nDCs after the Dipole Magnet");
  AddButton("TRK", "rt2dcx.C", "R-T_dcx",
    "rt2dcx.C - r+chi v.s. drift time for\nX wires of DCs after the Dipole Magnet");
  AddButton("TRK", "d2.C", "dSquare_DCTa",
    "d2.C - Sum of tracking chi^2 using only Wire\nPosition for DCs around the Target");
  AddButton("TRK", "d2dc.C", "dSquare_dc",
    "d2dc.C - Sum of tracking chi^2 using only Wire\nPosition for DCs after the Dipole Magnet");
  AddButton("TRK", "rr.C", "R_X1vX2_DCTa",
    "rr.C - Drift distance of X1 vs X2\nfor DCs around the Target");
  AddButton("TRK", "rrdcx.C", "R_X1vX2_dc",
    "rrdcx.C - Drift distance of X1 vs X2\nfor X Wires of DCs after the Dipole Magnet");
  AddButton("TRK", "gl.C", "GlobalTrking",
    "gl.C - Using Ameliorated Tracking Algorithm\ni.e., DCTa Tracking using postMag Info");
  
  AddButton("PID", "cpid.C", "Brho-Calibration", "cpid.C - Calibration constants fitting for RIBLL2-PID");
  AddButton("PID", "z.C", "RIBLL2-PID", "z.C - The ULTIMATE secondary beam PID result");


  // do decoding and mapping (+tracking or/and PID) first if rootfile is not ready
  if("" == fROOTFile) DeactivatePostAna();
} // end member function Initialize

void ETFGana::AddButtonGroup(const string &title){
  // check name duplication //
  for(auto &p : fvGF){
    if(!strcmp(title.data(), p->GetTitle()))
      er("ETFGana", "AddButtonGroup: duplicate button group name: %s", title.data());
  } // end for

  auto g = new TGGroupFrame(fPad, title.data());
  g->SetTitlePos(TGGroupFrame::kLeft);
  fPad->AddFrame(g, fLF);
  fvGF.push_back(g);
  fvvB.push_back(new vector<TGTextButton *>);
} // end member function AddButtonGroup

// cat: button category, e.g., "DAQ", "DET", "TRK" or "PID"
// mac: the macro in (for the time being) config/apr2023/cmd, e.g, dr.C
// tip: the descriptive tip to the button's function
void ETFGana::AddButton(const string &cat, const string &mac, const string &name,
const string &tip){
  // identify the button group //
  TGGroupFrame *gf = nullptr;
  int id = -1, i = 0; // the id of the button - for identification
  for(auto &p : fvGF){
    if(!strcmp(cat.data(), p->GetTitle())){
      gf = p;
      id = i*100;
      break;
    } // end if
    i++;
  } // end for
  if(!gf) er("ETFGana", "AddButton: button group %s not found", cat.data());

  // create the button //
  vector<TGTextButton *> &v = *fvvB[id/100];
  const int vs = (int)v.size();
  if(vs >= 100) er("ETFGana", "AddButton: buttons are too many: %d (>=100)", vs);
  const int bid = id + vs;
  auto b = new TGTextButton(gf, name.data(), bid);
  gf->AddFrame(b, fLB);
  b->Connect("Clicked()", "ETFGana", this, "ButtonReact(int)");
  b->SetToolTipText(tip.data());
  fMap[bid] = bt_t{.button = b, .macro = mac, .hasClicked = false};
  b->ChangeBackground(cyan());
  v.push_back(b);
} // end member function AddButton

// react to button with the id
void ETFGana::ButtonReact(int id){
  if(-1 == id){
    TGButton *btn = static_cast<TGButton *>(gTQSender);
    id = btn->WidgetId();
  } // end if

  if(!fMap.count(id)) er("ETFGana", "ButtonReact: unregistered button id: %d", id);
  bt_t &b = fMap.at(id);
  TGTextButton *btn = b.button;
  cout << "Pressed button: \033[42;1m" << btn->GetTitle() << "\033[0m" << endl;
  if(b.hasClicked && 0){
    const char *txt =
      form("%s: Has been executed already.\nDo you wanna run it again?",
        btn->GetTitle());
    new ETFGPrompt(gClient->GetRoot(), this, txt, false, &fUserOpt, 400, 200);
    // cout << "User Option: " << fUserOpt << endl;
    if(1 == fUserOpt){ // user confirmed to run a second time
      fUserOpt = 0;
      b.hasClicked = false;
      ButtonReact(btn->WidgetId());
    } // end if
  } // end if
  else{
    if("" == fROOTFile) er("ETFGana", "ButtonReact: rootfile is empty");
    // open a new terminal to run the root macro //
    const char *cmd = form("gnome-terminal -t %s_%s_%s -- "
      "bash -c \"./a.sh %s %s\"", btn->GetTitle(), fROOTFile.data(), b.macro.data(),
      fROOTFile.data(), b.macro.data());
    if(-1 == system(cmd)) ETFMsg::Info("ETFGana", "ButtonReact: %s", strerror(errno));
    const char *ccmd = form("./a.sh %s %s", fROOTFile.data(), b.macro.data());
    cout << "Command in execution: \033[32;1m" << ccmd << "\033[0m" << endl;
    b.hasClicked = true; // each one can be used only once
    btn->ChangeBackground(white());
  } // end else
} // end member function ButtonReact

// arrange for the new widgets and map windows
void ETFGana::Configure(){
  MapSubwindows(); // show the content
  Resize(GetDefaultSize()); // arrange them in proper position
  if(fGanaPre && fHeight < 800) fHeight = 800;
  Resize(fWidth, fHeight); // arrange them in proper position
  MapWindow(); // show the main window
} // end member function Configure

void ETFGana::CloseWindow(){
  cout << "\033[32;1m" << "Bye \033[33;1m:)" << "\033[0m" << endl;
  if(fGanaPre && 1 == fGanaPre->AnaStatus()){
    ETFMsg::SetIRP();
    do usleep(100);
    while(2 != fGanaPre->AnaStatus());
  } // end if
  if(-1 == system("ps x | grep \"./a.sh\" | grep -v grep | grep -v mule"
    " | awk '{print \"kill -9 \" $1}' | bash")){ // close all the opend windows
    ETFMsg::Info("ETFGana", "CloseWindow: %s", strerror(errno));
  } // end if
  gApplication->Terminate();
} // end member function CloseWindow

void ETFGana::SetROOTFile(const string &rtf){
  if("" != rtf) SetWindowName(form("%s - GANA - GUI Ana for ETF", rtf.data()));
  else SetWindowName("GANA - GUI Data Analysis Framework for ETF");
  fROOTFile = rtf;
} // end member function SetROOTFile

// activate buttons in fvvB
void ETFGana::ActivatePostAna(){
  for(auto &v : fvvB) for(auto &p : *v) p->SetEnabled(true);
  fStop->SetEnabled(false);
} // end member function ActivatePostAna

// deactivate buttons in fvvB
void ETFGana::DeactivatePostAna(){
  for(auto &v : fvvB) for(auto &p : *v) p->SetEnabled(false);
} // end member function DeactivatePostAna

// stop the ana process
void ETFGana::Stop(){
  ETFMsg::SetIRP();
  ActivatePostAna();
} // end member function Stop

void ETFGana::ActivateStopButton(){
  fStop->SetEnabled(true);
} // end member function ActivateStopButton

void ETFGana::Logo(){
  // ETFMsg::Logo();
  string s;
  ETFMsg::LogoPlain(s);
  new ETFGPrompt(gClient->GetRoot(), this, s, true, nullptr, 400, 200, kVerticalFrame);
} // end member function Logo
